import { track, trigger } from "./effect.js";

/**
 * @description 创建响应式对象
 * @param {*} data 源数据
 * @param {*} isShallow 是否为浅响应式
 * @returns
 */
function createReactive(data, isShallow = false, isReadonly = false) {
  /* 拦截for循环中，与副作用函数进行关联的key */
  let ITERATE_KEY = Symbol();

  /* 重写数组方法 */
  const arrayInstrumenttation = {};

  ["includes", "indexOf", "lastIndexOf"].forEach((method) => {
    /* 拷贝数组方法 */
    const originMethod = Array.prototype[method];

    /* 重写数组方法 */
    arrayInstrumenttation[method] = function (...args) {
      console.log(`执行重写的数组的${method}方法`);
      /* 先在代理对象自身查找是否存在 */
      let res = originMethod.apply(this, args);
      /* 如果代理对象自身没有找到 */
      /* 则到代理对象的原始对象上进行查找 */
      if (res === false || res === -1) {
        res = originMethod.apply(this.raw, args);
      }

      return res;
    };
  });

  /* 是否允许收集依赖（副作用函数） */
  let shouldTrack = true;

  ["push", "pop", "unshift", "shift", "splice"].forEach((method) => {
    const originMethod = Array.prototype[method];

    arrayInstrumenttation[method] = function (...args) {
      /* 在数组栈方法默认行为之前设置为false，表示禁止依赖收集 */
      shouldTrack = false;

      const res = originMethod.apply(this, args);

      /* 在数组站方法默认行为之后设置为true，表示进行依赖收集 */
      /* 避免由于栈方法内部读取设置length行为而导致栈溢出 */
      shouldTrack = true;

      return res;
    };
  });

  return new Proxy(data, {
    get(target, key, receiver) {
      if (key === "raw") {
        return target;
      }

      /* 如果target是数组，并且读取的key是存在于 重写的数组方法对象中 */
      /* 那么返回重写的数组方法 */
      if (Array.isArray(target) && arrayInstrumenttation[key]) {
        return Reflect.get(arrayInstrumenttation, key, receiver);
      }

      const res = Reflect.get(target, key, receiver);

      /* 如果是浅层响应，直接返回值 */
      if (isShallow) {
        return res;
      }

      /* 深响应：被访问的属性是对象 */
      if (typeof res === "object" && res !== null) {
        return isReadonly ? readonly(res) : reactive(res);
      }
      /* 非只读的属性，并且key不为symbol才进行依赖（副作用函数）收集 */
      if (!isReadonly && typeof key !== "symbol") {
        track(target, key, shouldTrack);
      }

      return res;
    },
    set(target, key, value, receiver) {
      console.log("设置了obj的属性");
      console.log(key);
      /* 只读 */
      if (isReadonly) {
        console.warn(`属性${key}是只读的`);
        return true;
      }

      const oldValue = target[key];

      /* 如果代理的是数组，则检测被设置的索引值是否小于数组长度 */
      /* 如果是则视为修改，否则是新增 */
      /* 如果代理的是对象，则检测被设置的属性存不存在于代理对象 */
      /* 如果存在则视为修改，否则为新增 */
      const type = Array.isArray(target)
        ? Number(key) < target.length
          ? "SET"
          : "ADD"
        : target.hasOwnProperty(key)
        ? "SET"
        : "ADD";

      const res = Reflect.set(target, key, value, receiver);
      /* 判断当前触发set函数的是否是对象本身 */
      if (target === receiver.raw) {
        /* 当新值不等于旧值并且新值或者旧值不为NaN */
        if (oldValue !== value && (oldValue === oldValue || value === value)) {
          trigger(target, { key, ITERATE_KEY }, type, value);
        }
      }

      return res;
    },
    has(target, key) {
      track(target, key);
      return Reflect.has(target, key);
    },
    ownKeys(target) {
      track(target, Array.isArray(target) ? "length" : ITERATE_KEY);
      return Reflect.ownKeys(target);
    },
    deleteProperty(target, key) {
      /* 只读 */
      if (isReadonly) {
        console.warn(`属性${key}是只读的`);
        return true;
      }

      const hadKey = target.hasOwnProperty(key);
      const res = Reflect.deleteProperty(target, key);
      if (hadKey && res) {
        trigger(target, { key, ITERATE_KEY }, "DELETE");
      }

      return res;
    },
  });
}

/* 存储原始对象到代理对象的映射 */
const reactiveMap = new Map();

/**
 * @description 创建深响应式对象
 * @param {Object} data 源数据
 */
export function reactive(obj) {
  /* 从映射表中获取原始对象的代理对象 */
  const existionProxy = reactiveMap.get(obj);
  /* 已存在则直接返回原始对象的代理对象 */
  if (existionProxy) {
    return existionProxy;
  }
  /* 否则创建新的代理对象 */
  const proxy = createReactive(obj);
  /* 创建原始对象到代理对象的映射关系，从而避免重复创建 */
  reactiveMap.set(obj, proxy);

  return proxy;
}

/**
 * @description 创建浅响应式对象
 * @param {*} obj 源数据
 */
export function shallowReactive(obj) {
  return createReactive(obj, true);
}

/**
 * @description 创建深只读响应式对象
 * @param {*} obj 源数据
 * @returns
 */
export function readonly(obj) {
  return createReactive(obj, false, true);
}

/**
 * @description 创建浅只读响应式对象
 * @param {*} obj 源数据
 * @returns
 */
export function shallowReadonly(obj) {
  return createReactive(obj, true, true);
}
